{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "HoloViews is designed to be both highly customizable, allowing you to control how your visualizations appear, but also to enforce a strong separation between your data (with any semantically associated metadata, like type, dimension names, and description) and all options related purely to visualization.  This separation allows HoloViews objects to be generated easily by external programs, without giving them a dependency on any plotting or windowing libraries.  It also helps make it completely clear which parts of your code deal with the actual data, and which are just about displaying it nicely, which becomes very important for complex visualizations that become more complicated than your data itself.\n",
    "\n",
    "To achieve this separation, HoloViews stores visualization options independently from your data, and applies the options only when rendering the data to a file on disk, a GUI window, or an IPython notebook cell.\n",
    "\n",
    "This tutorial gives an overview of the different types of options available, how to find out more about them, and how to set them in both regular Python and using the IPython magic interface that is shown elsewhere in the tutorials."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " First, we'll create some HoloViews data objects ready to visualize:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import holoviews as hv\n",
    "hv.notebook_extension()\n",
    "\n",
    "x,y = np.mgrid[-50:51, -50:51] * 0.1\n",
    "image = hv.Image(np.sin(x**2+y**2), group=\"Function\", label=\"Sine\") \n",
    "\n",
    "coords = [(0.1*i, np.sin(0.1*i)) for i in range(100)]\n",
    "curve = hv.Curve(coords)\n",
    "\n",
    "curves = {phase: hv.Curve([(0.1*i, np.sin(phase+0.1*i)) for i in range(100)])\n",
    "          for phase in [0, np.pi/2, np.pi, np.pi*3/2]}\n",
    "\n",
    "waves = hv.HoloMap(curves)\n",
    "\n",
    "layout = image + curve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Rendering and saving objects from Python <a id='python-saving'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To illustrate how to do plotting independently of IPython, we'll generate and save a plot directly to disk.  First, let's create a ``renderer`` object that will render our files to SVG (for static figures) or GIF (for animations):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "renderer = hv.Store.renderers['matplotlib'].instance(fig='svg', holomap='gif')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We could instead have used the default ``Store.renderer``, but that would have been PNG format.  Using this renderer, we can save any HoloViews object as SVG or GIF:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "renderer.save(layout, 'example_I')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's it!  The renderer builds the figure in matplotlib, renders it to SVG, and saves that to \"example_I.svg\" on disk.  Everything up to this point would have worked the same in IPython or in regular Python, even with no display available.  But since we're in IPython Notebook at the moment, we can check whether the exporting worked, by loading the file back into the notebook:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import SVG\n",
    "SVG(filename='example_I.svg')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use this workflow for generating HoloViews visualizations directly from Python, perhaps as a part of a set of scripts that you run automatically, e.g. to put your results up on a web server as soon as data is generated.  But so far, this plot just uses all the default options, with no customization.  How can we change how the plot will appear when we render it?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HoloViews visualization options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "HoloViews provides three categories of visualization options that can be set by the user.  In this section we will first describe the different kinds of options, then later sections show you how to list the supported options of each type for a given HoloViews object or class, and how to change them in Python or IPython."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``style`` options:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``style`` options are passed directly to the underlying rendering backend that actually draws the plots, allowing you to control the details of how it behaves.  The default backend is matplotlib, but there are other backends either using matplotlib's options (e.g. ``nbagg``), or their own sets of options (e.g. [``bokeh``](Bokeh_Backend) ).\n",
    "\n",
    "For whichever backend has been selected, HoloViews can tell you which options are supported, but you will need to see the plotting library's own documentation (e.g. [matplotlib](http://matplotlib.org/contents.html), [bokeh](http://bokeh.pydata.org)) for the details of their use.\n",
    "\n",
    "HoloViews has been designed to be easily extensible to additional backends in the future, such as [Plotly](https://github.com/ioam/holoviews/pull/398), Cairo, VTK, or D3.js, and if one of those backends were selected then the supported style options would differ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``plot`` options:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Each of the various HoloViews plotting classes declares various [Parameters](http://ioam.github.io/param) that control how HoloViews builds the visualization for that type of object, such as plot sizes and labels.  HoloViews uses these options internally; they are not simply passed to the underlying backend.  HoloViews documents these options fully in its online help and in the [Reference Manual](http://holoviews.org/Reference_Manual).  These options may vary for different backends in some cases, depending on the support available both in that library and in the HoloViews interface to it, but we try to keep any options that are meaningful for a variety of backends the same for all of them."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### ``norm`` options:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``norm`` options are a special type of plot option that are applied orthogonally to the above two types, to control normalization.  Normalization refers to adjusting the properties of one plot relative to those of another.  For instance, two images normalized together would appear with relative brightness levels, with the brightest image using the full range black to white, while the other image is scaled proportionally.  Two images normalized independently would both cover the full range from black to white.  Similarly, two axis ranges normalized together will expand to fit the largest range of either axis, while those normalized separately would cover different ranges.\n",
    "\n",
    "There are currently only two ``norm`` options supported, ``axiswise`` and ``framewise``, but they can be applied to any of the various object types in HoloViews to specify a huge range of different normalization options.\n",
    "\n",
    "For a given category or group of HoloViews objects, if ``axiswise`` is True, normalization will be computed independently for all items in that category that have their own axes, such as different ``Image`` plots or ``Curve`` plots. If ``axiswise`` is False, all such objects are normalized together.\n",
    "\n",
    "For a given category or group of HoloViews objects, if ``framewise`` is True, normalization of any ``HoloMap`` objects included is done independently per frame rendered -- each frame will appear as it would if it were extracted from the ``HoloMap`` and plotted separately.  If ``framewise`` is False (the default), all frames in a given ``HoloMap`` are normalized together, so that you can see strength differences over the course of the animation.\n",
    "\n",
    "As described below, these options can be controlled precisely and in any combination to make sure that HoloViews displays the data of most interest, ignoring irrelevant differences and highlighting important ones."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Finding out which options are available for an object"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the ``norm`` options, no further online documentation is provided, because all of the various visualization classes support only the two options described above.  But there are a variety of ways to get the list of supported ``style`` options and detailed documentation for the ``plot`` options for a given component.\n",
    "\n",
    "First, for any Python class or object in HoloViews, you can use ``holoviews.help(``*object-or-class*``, visualization=False)`` to find out about its parameters. For instance, these parameters are available for our ``Image`` object, shown with their current value (or default value, for a class), data type, whether it can be changed by the user (if it is constant, read-only, etc.), and bounds if any:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.help(image, visualization=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This information can be useful, but we have explicitly suppressed information regarding the visualization parameters -- these all report metadata about your data, not about anything to do with plotting directly.  That's because the normal HoloViews components have nothing to do with plotting; they are just simple containers for your data and a small amount of metadata.\n",
    "\n",
    "Instead, the plotting implementation and its associated parameters are kept in completely separate Python classes and objects.  To find out about *visualizing* a HoloViews component like an ``Image``, you can simply use the help command ``holoviews.help(``*object-or-class*``)`` that looks up the code that plots that particular type of component, and then reports the ``style`` and ``plot`` options available for it.\n",
    "\n",
    "For our ``image`` example, ``holoviews.help`` first finds that ``image`` is of type ``Image``, then looks in its database to find that ``Image`` visualization is handled by the ``RasterPlot`` class (which users otherwise rarely need to access directly).  ``holoviews.help`` then shows information about what objects are available to customize (either the object itself, or the items inside a container), followed by a brief list of ``style`` options supported by a ``RasterPlot``, and a list of ``plot`` options (which are all the [parameters](http://ioam.github.io/param) of a ``RasterPlot``). As this list of ``plot`` options is very long by default, here is an example that uses the ``pattern`` argument to limit the results to the options referencing the string ``'bounds'``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.help(image, pattern='bounds')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The pattern option is particularly useful in conjunction with ``recursive=True`` which helps when searching for information across the different levels of a composite object. Note that the ``pattern`` argument supports Python's regular expression syntax and may also be used together with the ``visualization=False`` option."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Supported ``style`` options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, HoloViews lists the currently allowed ``style`` options, but provides no further documentation because these settings are implemented by matplotlib and described at the matplotlib site.  Note that matplotlib actually accepts a huge range of additional options, but they are not listed as being allowed because those options are not normally meaningful for this plot type.  But if you know of a specific matplotlib option not on the list and really want to use it, you can add it manually to the list of supported options using ``Store.add_style_opts(``*holoviews-component-class*``, ['``*matplotlib-option* ...``'])``.  For instance, if you want to use the ``filternorm`` parameter with this image object, you would run ``Store.add_style_opts(Image, ['filternorm'])``.  This will add the new option to the corresponding plotting class ``RasterPlot``, ready for use just like any other style option: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hv.Store.add_style_opts(hv.Image, ['filternorm'])\n",
    "# To check that it worked:\n",
    "RasterPlot = renderer.plotting_class(hv.Image)\n",
    "print(RasterPlot.style_opts)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Changing ``plot`` options at the class level"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Any [parameter](http://ioam.github.io/param) in HoloViews can be set on an object or on the class of the object, so any of the above plot options can be set like: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "RasterPlot.colorbar=True\n",
    "RasterPlot.set_param(show_title=False,show_frame=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here ``.set_param()`` allows you to set multiple parameters conveniently, but it works the same as the single-parameter ``.colorbar`` example above it.  Setting these values at the class level affects all previously created and to-be-created plotting objects of this type, unless specifically overridden via ``Store`` as described below.\n",
    "\n",
    "Note that if you look at the source code for a particular plotting class, you will only see *some* of the parameters it supports.  The rest, such as ``show_frame`` above, are defined in a superclass of the given object.  The [Reference Manual](http://holoviews.org/Reference_Manual) shows the complete list of parameters available for any given class (those labeled ``param`` in the manual), but it can be an overwhelming list since it includes all superclasses, all the metadata about each parameter, etc.  The ``holoviews.help`` command with ``visualization=True`` not only provides a much more concise listing, it can will also provide ``style`` options not available in the Reference Manual, by using the database to determine which plotting class is associated with this object.\n",
    "\n",
    "Because setting these parameters at the class level does not provide much control over individual plots, HoloViews provides a much more flexible system using the ``OptionTree`` mechanisms described below, which can override these class defaults according to the more specific HoloViews object type, ``group``, and ``label`` attributes.  \n",
    "\n",
    "The rest of the sections show how to change any of the above options, once you have found the right one using the suitable call to ``holoviews.help``."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Controlling options from Python"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once you know the name of the option you want to change, and the value you want to change it to, there are a number of ways to customize your plot.  \n",
    "\n",
    "For the Python output to SVG example above, you can specify the options for a given type using keywords supplying a dictionary for any of the above option categories. You can see that the colormap changes when we supply that ``style`` option and render a new SVG:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "renderer.save(layout, 'example_II', style=dict(Image={'cmap':'Blues'}),\n",
    "                                    plot= dict(Image={'yaxis':None}))\n",
    "SVG(filename='example_II.svg')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As before, the SVG call is simply to display it here in the notebook; the actual image is saved on disk and then loaded back in here for display. \n",
    "\n",
    "You can see that the image now has a colorbar, because we set ``colorbar=True`` on the ``RasterPlot`` class, that it has become blue, because we set the matplotlib ``cmap`` style option in the ``renderer.save`` call, and that the *y* axis has been disabled, because we set the ``plot`` option ``yaxis`` to ``None`` (which is normally ``'left'`` by default, as you can see in the default value for ``RasterPlot``'s parameter ``yaxis`` above).  Hopefully you can see that once you know the option value you want to use, it can be provided easily.\n",
    "\n",
    "You can also create a whole set of options separately, perhaps holding a large collection of preferred values, and apply it whenever you wish to save:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "options={'Image.Function.Sine': {'plot':dict(fig_size=50), 'style':dict(cmap='jet')}}\n",
    "renderer.save(layout, 'example_III',options=options)\n",
    "SVG(filename='example_III.svg')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here you can see that the *y* axis has returned, because our previous setting to turn it off was just for the call to ``renderer.save``.  But we still have a colorbar, because that parameter was set at the class level, for all future plots of this type.  Note that this form of option setting, while more verbose, accepts the full ``{type}[.{group}[.{label}]]`` syntax, like ``'Image.Function.Sine'`` or ``'Image.Function'``, while the shorter keyword approach above only supports the class, like 'Image'.\n",
    "\n",
    "Note that for the ``options`` dictionary, the option nesting is inverted compared to the keyword approach: the outermost dictionary is by key (``Image``, or ``Image.Function.Sines``), with the option categories underneath.  You can see that with this mechanism, we can specify the options even for subobjects of a container, as long as we can specify them with an appropriate key.\n",
    "\n",
    "There's also another way to customize options in Python that lets you build up customizations incrementally.  To do this, you can associate a particular set of options persistently with a particular HoloViews object, even if that object is later combined with other objects into a container.  Here a new copy of the object is created, with the given set of options (using either the keyword or ``options=`` format above) bound to it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "green_sine = image.opts(style={'cmap':'Greens'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we could save the object to SVG just as before, but in this case we can skip a step and simply view it directly in the notebook:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "green_sine"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To customize options of individual components in composite objects like Overlays or Layouts you can either specify the options on each individual component or specify which object to customize using the ``{type}[.{group}[.{label}]]`` syntax."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "(image + curve).opts(style={'Image.Function.Sine': dict(cmap='Reds'), 'Curve': dict(color='indianred')})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Both IPython notebook and ``renderer.save()`` use the same mechanisms for keeping track of the options, so they will give the same results.  Specifically, what happens when you \"bind\" a set of options to an object is that there is an integer ID stored in the object (``green_sine`` in this case), and a corresponding entry with that ID is stored in a database of options called an ``OptionTree`` (kept in ``holoviews.core.options.Store``).  The object itself is otherwise unchanged, but then if that object is later used in another container, etc. it will retain its ID and therefore its customization.  Any customization stored in an ``OptionTree`` will override any class attribute defaults set like ``RasterGridPlot.border=5`` above. This approach lets HoloViews keep track of any customizations you want to make, without ever affecting your actual data objects.\n",
    "\n",
    "If the same object is later customized again to create a new customized object, the old customizations will be copied, and then the new customizations applied.  The new customizations will thus override the old, while retaining any previous customizations not specified in the new step.  \n",
    "\n",
    "In this way, it is possible to build complex objects with arbitrary customization, step by step.  As mentioned above, it is also possible to customize objects already combined into a complex container, just by specifying an option for a suitable key (e.g. ``'Image.Function.Sine'`` above).  This flexible system should allow for any level of customization that is needed.\n",
    "\n",
    "Finally, there is one more way to apply options that is a mix of the above approaches -- temporarily assign a new ID to the object and apply a set of customizations during a specific portion of the code. To illustrate this, we'll create a new Image object called 'Cosine':"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cosine = hv.Image(np.cos(x**2+y**2), group=\"Function\", label=\"Cosine\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with hv.StoreOptions.options(cosine, options={'Image':{'style':{'cmap':'Reds'}}}):\n",
    "    data, info = renderer(cosine)\n",
    "print(info)\n",
    "SVG(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here the result is in red as it was generated in the context of a 'Reds' colormap but if we display cosine again outside the scope of the with statement, it retains the default settings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cosine"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that if we want to use this context manager to set new options on the existing green_sine object, you must specify that the options apply to a specific Image by stating the applicable group and label:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with hv.StoreOptions.options(green_sine, options={'Image.Function.Sine':{'style':{'cmap':'Purples'}}}):\n",
    "    data, info = renderer(green_sine)\n",
    "print(info)\n",
    "SVG(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the result inside the context is purple but elswhere green_sine remains green. If the group and label had not been specified above, the specific customization applied earlier (setting the green colormap) would take precedence over the general settings of Image. For this reason, it is important to know the appropriate precedence of new customizations, or else you can just always specify the object group and label to make sure the new settings override the old ones."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Controlling options in IPython using ``%%opts`` and ``%opts``"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The above sections describe how to set all of the options using regular Python.  Similar functionality is provided in IPython, but with a more convenient syntax based on an IPython magic command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Curve style(linewidth=8) Image style(interpolation='bilinear') plot[yaxis=None] norm{+framewise}\n",
    "layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``%%opts`` magic works like the pure-Python option for associating options with an object, except that it works on the item in the IPython cell, and it affects the item directly rather than making a copy or applying only in scope.  Specifically, it assigns a new ID number to the object returned from this cell, and makes a new ``OptionTree`` containing the options for that ID number.\n",
    "\n",
    "If the same ``layout`` object is used later in the notebook, even within a complicated container object, it will retain the options set on it.\n",
    "\n",
    "The options accepted are just the same as for the Python version, but specified more succinctly:\n",
    "\n",
    "``%%opts`` *target-specification*  ``style(``*styleoption*``=``*val* ...``) plot[``*plotoption*``=``*val* ...``] norm{+``*normoption* ``-``*normoption*...``}``\n",
    "\n",
    "Here *key* lets you specify the object type (e.g. ``Image``), and optionally its ``group`` (e.g. ``Image.Function``) or even both ``group`` and ``label`` (e.g. ``Image.Function.Sine``), if you want to control options very precisely. There is also an even further abbreviated syntax, because the special bracket types alone are enough to indicate which category of option is specified:\n",
    "\n",
    "``%%opts`` *target-specification*  ``(``*styleoption*``=``*val* ...``) [``*plotoption*``=``*val* ...``] {+``*normoption* ``-``*normoption* ...``}``\n",
    "\n",
    "Here parentheses indicate style options, square brackets indicate plot options, and curly brackets indicate norm options (with ``+axiswise`` and ``+framewise`` indicating True for those values, and ``-axiswise`` and ``-framewise`` indicating False).  Additional *target-specification*s and associated options of each type for that *target-specification* can be supplied at the end of this line.  This ultra-concise syntax is used throughout the other tutorials, because it helps minimize the code needed to specify the plotting options, and helps make it very clear that these options are handled separately from the actual data.\n",
    "\n",
    "Here we demonstrate the concise syntax by customizing the style and plot options of the ``Curve`` in the layout:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Curve (color='r') [fontsize={'xlabel':15, 'ticks':8}] \n",
    "layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The color of the curve has been changed to red and the fontsizes of the x-axis label and all the tick labels have been modified. The ``fontsize`` is an important plot option, and you can find more information about the available options in the ``fontsize`` documentation above.\n",
    "\n",
    "The ``%%opts`` magic is designed to allow incremental customization, which explains why the curve in the cell above has retained the increased thickness specified earlier. To reset all the customizations that have been applied to an object, you can create a fresh, uncustomized copy as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "layout.opts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``%opts`` \"line\" magic (with one ``%``) works just the same as the ``%%opts`` \"cell\" magic, but it changes the global default options for all future cells, allowing you to choose a new default colormap, line width, etc.\n",
    "\n",
    "Apart from its brevity, a big benefit of using the IPython magic syntax ``%%opts`` or ``%opts`` is that it is fully tab-completable.  Each of the options that is currently available will be listed if you press ``<TAB>`` when you are ready to write it, which makes it much easier to find the right parameter.  Of course, you will still need to consult the full ``holoviews.help`` documentation (described above) to see the type, allowable values, and documentation for each option, but the tab completion should at least get you started and is great for helping you remember the list of options and see which options are available.\n",
    "\n",
    "You can even use the succinct IPython-style specification directly in your Python code if you wish, but it requires the external [pyparsing](https://pypi.python.org/pypi/pyparsing) library (which is already available if you are using matplotlib):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from holoviews.util.parser import OptsSpec\n",
    "renderer.save(image + waves, 'example_V', \n",
    "              options=OptsSpec.parse(\"Image (cmap='gray')\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is also a special IPython syntax for listing the visualization options for a plotting object in a pop-up window that is equivalent to calling ``holoviews.help(``*object*``)``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output info=True\n",
    "curve"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The line-magic version of this syntax ``%output info=True`` is particularly useful for learning about components using the notebook, because it will keep a window open with the available options for each object updated as you do ``<shift-Enter>`` in each cell.  E.g. you can go through each of the components in the ``Elements`` or ``Containers`` tutorials in this way, to see what options are offered by each without having to type anything for each one."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Option inheritance\n",
    "\n",
    "Since HoloViews objects nest in a variety of ways and you do not want to keep changing the options specification when you compose your object into an ``Overlay`` or ``NdOverlay``, certain ``plot`` options are inherited. This includes all plot options which control the appearance of the axes, but not those that are specific to the Element. As a simple example let us combine the ``Image`` from above with some ``Bounds``. Even though we apply the ``xrotation`` and ``yticks`` options to the ``Image`` they are inherited by the ``Overlay`` of the ``Image`` and ``Bounds``."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Image [xrotation=90 yticks=[-0.5, 0., 0.5]]\n",
    "image * hv.Bounds((-.25, -.25, .25, .25))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Separating data and visualization options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hopefully you see from this tutorial how HoloViews enforces a strict separation between your data, stored in HoloViews ``Element`` and container objects, and your plotting options, stored in dictionaries or ``OptionTree``s.  Finding the right options is easiest in IPython, because of ``<TAB>`` completion, but the same options are available in pure Python as well, with or without a display, allowing you to automate any part of the process of visualization and analysis."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
